/*******************************************************************************
 * Copyright (c) 2011, 2016 Eurotech and/or its affiliates
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     Eurotech
 *******************************************************************************/
package org.eclipse.kura.linux.position;

/**
 * Implements NMEA sentences parser functions.
 *
 */
public class NMEAParser {

    private int m_fixQuality;
    private String m_timeNmea;
    private String m_dateNmea;
    private double m_longNmea;
    private double m_latNmea;
    private double m_speedNmea;
    private double m_altNmea;
    private double m_trackNmea;
    private double m_DOPNmea;
    private double m_PDOPNmea;
    private double m_HDOPNmea;
    private double m_VDOPNmea;
    private int m_3DfixNmea;
    private int m_nrSatellites;
    private static boolean m_validPosition;

    /**
     * Fill the fields of GPS position depending of the type of the sentence
     *
     * @param sentence
     *            most recent sentence String from GPS modem
     */
    public void parseSentence(String sentence) {
        // first remove the end "*"+chksum
        int starpos = sentence.indexOf('*');
        String s_sentence = sentence.substring(0, starpos);

        String[] tokens = s_sentence.split(",");

        /*
         * Starting from 4.0 NMEA specs the GPS device can send messages representing different talkers
         *
         * $GP = GPS
         * $GS = Glonass
         * $GN = GNSS, that is GPS + Glonass + possibly others
         */
        if (!tokens[0].startsWith("$G")) {
            // Not a valid token. Return.
            return;
        }

        if (tokens[0].endsWith("GGA")) {
            if (tokens.length > 9) {
                m_validPosition = true;
                if (!tokens[1].isEmpty()) {
                    this.m_timeNmea = tokens[1];
                } else {
                    m_validPosition = false;
                }
                if (!tokens[2].isEmpty()) {
                    this.m_latNmea = convertPositionlat(tokens[2], tokens[3]);
                } else {
                    m_validPosition = false;
                }
                if (!tokens[4].isEmpty()) {
                    this.m_longNmea = convertPositionlon(tokens[4], tokens[5]);
                } else {
                    m_validPosition = false;
                }
                if (!tokens[6].isEmpty()) {
                    this.m_fixQuality = Integer.parseInt(tokens[6]);
                    if (this.m_fixQuality == 0) {
                        m_validPosition = false;
                    }
                } else {
                    m_validPosition = false;
                }
                if (!tokens[7].isEmpty()) {
                    this.m_nrSatellites = Integer.parseInt(tokens[7]);
                } else {
                    m_validPosition = false;
                }
                if (!tokens[8].isEmpty()) {
                    this.m_DOPNmea = Double.parseDouble(tokens[8]);
                } else {
                    m_validPosition = false;
                }
                if (!tokens[9].isEmpty()) {
                    this.m_altNmea = Double.parseDouble(tokens[9]);
                } else {
                    m_validPosition = false;
                }
            } else {
                m_validPosition = false;
            }
        } else if (tokens[0].endsWith("GLL")) {
            if (tokens.length > 5) {
                m_validPosition = true;
                if (!tokens[1].isEmpty()) {
                    this.m_latNmea = convertPositionlat(tokens[1], tokens[2]);
                } else {
                    m_validPosition = false;
                }
                if (!tokens[3].isEmpty()) {
                    this.m_longNmea = convertPositionlon(tokens[3], tokens[4]);
                } else {
                    m_validPosition = false;
                }
                if (!tokens[5].isEmpty()) {
                    this.m_timeNmea = tokens[5];
                } else {
                    m_validPosition = false;
                }
                if (!tokens[6].isEmpty()) { // check validity
                    if (!new String("A").equals(tokens[6])) {
                        m_validPosition = false;
                    }
                } else {
                    m_validPosition = false;
                }
            } else {
                m_validPosition = false;
            }
        } else if (tokens[0].endsWith("RMC")) {
            if (tokens.length > 8) {
                m_validPosition = true;
                if (!tokens[1].isEmpty()) {
                    this.m_timeNmea = tokens[1];
                }
                if (!tokens[2].isEmpty()) { // check validity
                    if (!new String("A").equals(tokens[2])) {
                        m_validPosition = false;
                    }
                } else {
                    m_validPosition = false;
                }
                if (!tokens[3].isEmpty()) {
                    this.m_latNmea = convertPositionlat(tokens[3], tokens[4]);
                } else {
                    m_validPosition = false;
                }
                if (!tokens[5].isEmpty()) {
                    this.m_longNmea = convertPositionlon(tokens[5], tokens[6]);
                } else {
                    m_validPosition = false;
                }
                if (!tokens[7].isEmpty()) {
                    this.m_speedNmea = Double.parseDouble(tokens[7]) / 1.94384449; // conversion speed in knots to m/s :
                                                                                   // 1
                }
                // m/s = 1.94384449 knots
                if (!tokens[8].isEmpty()) {
                    this.m_trackNmea = Double.parseDouble(tokens[8]);
                }
                if (!tokens[9].isEmpty()) {
                    this.m_dateNmea = tokens[9];
                } else {
                    m_validPosition = false;
                }
            } else {
                m_validPosition = false;
            }
        } else if (tokens[0].endsWith("GSA")) {
            if (tokens.length > 5) {
                m_validPosition = true;
                if (!tokens[2].isEmpty()) {
                    this.m_3DfixNmea = Integer.parseInt(tokens[2]);
                    if (this.m_3DfixNmea == 1) {
                        m_validPosition = false;
                    }
                } else {
                    m_validPosition = false;
                }
                int index = tokens.length - 3;
                if (!tokens[index].isEmpty()) {
                    this.m_PDOPNmea = Double.parseDouble(tokens[index]);
                } else {
                    m_validPosition = false;
                }
                if (!tokens[index + 1].isEmpty()) {
                    this.m_HDOPNmea = Double.parseDouble(tokens[index + 1]);
                } else {
                    m_validPosition = false;
                }
                if (!tokens[index + 2].isEmpty()) {
                    this.m_VDOPNmea = Double.parseDouble(tokens[index + 2]);
                } else {
                    m_validPosition = false;
                }
            } else {
                m_validPosition = false;
            }
        } else if (tokens[0].endsWith("VTG")) {
            if (tokens.length > 7 && !tokens[7].isEmpty()) {
                this.m_speedNmea = Double.parseDouble(tokens[7]) * 0.277777778; // conversion km/h in m/s : 1 km/h ->
                // 0,277777778 m/s;
            }
        }
    }

    double convertPositionlat(String pos, String direction) {
        double floatLatDegrees = 0;
        double floatLatMinutes = 0;
        String s;

        if (pos.length() < 6) {
            return 0;
        }

        // This copies the arrays to temporary arrays
        s = pos.substring(0, 2);
        floatLatDegrees = Double.parseDouble(s);
        s = pos.substring(2);
        floatLatMinutes = Double.parseDouble(s);
        floatLatDegrees = floatLatDegrees + floatLatMinutes / 60;
        if (direction.contains("S")) {
            floatLatDegrees = floatLatDegrees * -1;
        }
        return floatLatDegrees;
    }

    double convertPositionlon(String pos, String direction) {
        double floatLonDegrees = 0;
        double floatLonMinutes = 0;
        String s;

        if (pos.length() < 6) {
            return 0;
        }

        // This copies the arrays to temporary arrays
        s = pos.substring(0, 3);
        floatLonDegrees = Double.parseDouble(s);
        s = pos.substring(3);
        floatLonMinutes = Double.parseDouble(s);
        floatLonDegrees = floatLonDegrees + floatLonMinutes / 60;
        if (direction.contains("W")) {
            floatLonDegrees = floatLonDegrees * -1;
        }
        return floatLonDegrees;
    }

    public String get_timeNmea() {
        return this.m_timeNmea;
    }

    public int get_fixQuality() {
        return this.m_fixQuality;
    }

    public String get_dateNmea() {
        return this.m_dateNmea;
    }

    public double get_longNmea() {
        return this.m_longNmea;
    }

    public double get_latNmea() {
        return this.m_latNmea;
    }

    public double get_speedNmea() {
        return this.m_speedNmea;
    }

    public double get_altNmea() {
        return this.m_altNmea;
    }

    public double get_trackNmea() {
        return this.m_trackNmea;
    }

    public double get_DOPNmea() {
        return this.m_DOPNmea;
    }

    public double get_PDOPNmea() {
        return this.m_PDOPNmea;
    }

    public double get_HDOPNmea() {
        return this.m_HDOPNmea;
    }

    public double get_VDOPNmea() {
        return this.m_VDOPNmea;
    }

    public int get_3DfixNmea() {
        return this.m_3DfixNmea;
    }

    public int get_nrSatellites() {
        return this.m_nrSatellites;
    }

    public boolean is_validPosition() {
        return m_validPosition;
    }

}
